#include "StdAfx.h"
#include "Kernel.h"

#include <iostream>
#include <stdio.h>

#include <irrlicht.h>

irr::IrrlichtDevice*            g_IrrDevice;     // Irr Link;

/** Max frame skip */
const int MAX_FRAMESKIP = 5;

#define SAFE_DELETE( p ) { if(p) { delete p; p = NULL; } }


/**
    Initialize the Rendering Subsystem
 */
void Kernel::InitVideo()
{
    ViewProperties video;
    video.width = m_cvars.GetFloatValue( "v_width" );
    video.height = m_cvars.GetFloatValue( "v_height" );
    video.frequency = m_cvars.GetFloatValue( "v_freq" );

    video.fullscreen = m_cvars.GetFloatValue( "v_fullscreen" ) ? true : false;
    video.vsync = m_cvars.GetFloatValue( "v_vsync" ) ? true : false;

    video.windowTitle = m_cvars.GetStringValue( "v_windowtitle" );
    video.type = VF_IRRLICHT_DX9; // TEMP
    video.closeEvent = &Kernel::StopLoop;

    m_renderer = new RenderManager;
    if ( ! m_renderer )
    {
        LPrintf( "*** ERROR:: Memory allocation error initializing the Renderer! ***" );
        exit(1);
    }

    if ( m_renderer->Init( video ) )
    {
        LPrintf( "*** ERROR:: The Renderer errored while initializing, attempting to continue... ***" );
        exit(1);
    }

    m_subsystems |= SF_RENDERER;    // mark as initialized
}
/**
    Initialize the Input Subsystem
*/
void Kernel::InitInput()
{
    m_input = new IrrInput;

    InputProperties input;
    input.joystickAvailible = m_cvars.GetFloatValue( "i_joystick" ) > 0;
    input.registerInput     = m_cvars.GetFloatValue( "i_input" ) > 0;
    
    if ( m_input->Init( input ) )
    {
        LPrintf( "FATAL ERROR:: Initializing the Input System! " );
        exit(1);
    }
    else
    {
        //m_input->AddKeyListener( &m_console );
    }

    m_subsystems |= SF_INPUT;    // mark as initialized
}

/**
    Initialize the Virtual File System
   
*/
void Kernel::InitVFS()
{
    // TODO - check cvars for type of FileSys
    m_fileSystem = new IrrVFSystem;
    if ( !m_fileSystem->Init() )
    {
        LPrintf( "FATAL ERROR:: Initializing the FVS! " );
        exit(1);
    }

    m_subsystems |= SF_VFS;
}

/**
    Initialize the scripting system
*/
void Kernel::InitScript()
{
    // TODO - check cvars for script engine
    m_scripter = new LuaEngine;
    if ( m_scripter->Init() )
    {
        LPrintf( "FATAL ERROR:: Initializing the Script Subsystem! " );
    }

    m_subsystems |= SF_SCRIPT;
}

/**
    Initialize the sound system
*/
void Kernel::InitSound()
{
    m_sound = new SoundManager;
    if ( m_sound->Init() )
    {
        LPrintf( "FATAL ERROR:: Initializing the Sound Subsystem! " );
    }
    else
    {
        m_sound->SetVolume( m_cvars.GetFloatValue( "s_mainVolume" ) );
    }
    m_subsystems |= SF_SOUND;
}


Kernel::Kernel(void)
{
    m_quit          = false;
    m_fixedTimeStep = false;
    m_delta         = 0.0f;
    m_lastFrame     = 0.0f;
    m_nextTick      = 0.0f;
    m_subsystems    = 0;

    m_input         = NULL;
    m_renderer      = NULL;
    m_fileSystem    = NULL;
    m_scripter      = NULL;
    m_sound         = NULL;
    m_game          = NULL;

    m_percisionTimer.Refresh();
}

Kernel::~Kernel(void)
{
    SAFE_DELETE( m_input );
    SAFE_DELETE( m_renderer );
    SAFE_DELETE( m_fileSystem );
    SAFE_DELETE( m_scripter );
    SAFE_DELETE( m_sound );
    SAFE_DELETE( m_game );
}

/**
========================
Init the Engine based off of the 
config file
========================
*/
int Kernel::Init(const std::string &config)
{
    m_console.Init();
    m_cvars.Init( this );


    m_console.Print( (_PROGRAM_ + _VERSION_ + "-- "+ _OS_ + "-- "+ __DATE__+ " " + " " __TIME__ ).c_str() ); 

    m_console.Print( ("Loading config file: " + config).c_str() );
    m_console.Exec( ("exec " + config).c_str() );                   // load Cvars
    m_game = new GameManager;
    m_game->Init();
    
    /**
        Create the Rendering System
    */
    InitVideo();
    /** 
        Init the scripting system
    */
    InitScript();
    /**
        Create the File System
    */
    InitVFS();

    /**
        Create the Input System
    */
    InitInput();
    if ( m_input ) 
    {
        m_input->AddKeyListener( m_game );
        m_input->AddMouseListener( m_game );
    }

    /** 
        Create the Sound System
    */
    InitSound();




    m_console.Exec( ("exec " + config).c_str() );                   // load again HACK!
    m_console.SetLog ( m_cvars.GetFloatValue("d_logger") > 0 );

    return 0;
}

/** 
========================
Shutdown the engine, clean up
========================
*/
void Kernel::Shutdown()
{
    if ( m_subsystems & SF_SOUND )
        m_sound->Shutdown();

    if ( m_subsystems & SF_INPUT )
        m_input->Shutdown();

    if ( m_subsystems & SF_VFS )
        m_fileSystem->Shutdown();

    if ( m_subsystems & SF_SCRIPT )
        m_scripter->Shutdown();

    if ( m_subsystems & SF_RENDERER )
        m_renderer->Shutdown();

    if ( m_game ) m_game->Shutdown();


    if ( g_IrrDevice )
    {
        g_IrrDevice->closeDevice();
        g_IrrDevice->drop();    
    }

}

/** 
========================
Run the engine
========================
*/
void Kernel::Execute()
{
    while( !m_quit )
    {
        Frame();
    }
}


/** 
========================
Run a Frame 
========================
*/
void Kernel::Frame()
{
    m_percisionTimer.Refresh();
    // calculate how long it was since we last came round this loop
    // and hold on to it so we can let the updating/rendering routine
    // know how much time to update by
    m_delta = GetTime() - m_lastFrame;
    m_lastFrame = GetTime();

    if ( m_subsystems & SF_INPUT )
        m_input->Update( m_delta );

    if ( m_fixedTimeStep )
    {
        // Catch up on the physics updates if needed,
        // have a constant deltaTime
        int loops = 0;
        float physicsDelta = m_delta;
        float timeStep = m_cvars.GetFloatValue( "d_timestep" );
        while( timeStep >= physicsDelta )
        {
            m_game->Update( timeStep );
            physicsDelta += timeStep;
        }
    } else m_game->Update( m_delta );

    if ( m_subsystems & SF_RENDERER )
    {
        m_renderer->Update( m_delta );
    }

    if ( m_subsystems & SF_SOUND )
    {
        m_sound->Update( m_delta );
    }
}


/** 
========================
Get the current time
========================
*/
float Kernel::GetTime()
{
    return m_percisionTimer.GetTime();
}


/**
========================
Get the FrameTime - frametime is one game loop
========================
*/
float Kernel::GetFrameTimeMS()
{
    return m_percisionTimer.GetFrameTime() * 1000.0f;
}

/** Print to the console */
void Kernel::Printf( const char *s, ... )
{
    char buffer[1024]; 
	va_list arguments;
    // parse out the arguments, and put them in 
    // string format
	va_start(arguments, s);
	      //  _vsnprintf_s(buffer, sizeof(buffer), s, arguments);
	    vsprintf(buffer, s, arguments);
    va_end(arguments);

    m_console.Print( buffer );
}

/** Print to the console and log the contents */
void Kernel::LPrintf( const char *s, ... )
{
    char buffer[1024]; 
	va_list arguments;
    // parse out the arguments, and put them in 
    // string format
	va_start(arguments, s);
	      //  _vsnprintf_s(buffer, sizeof(buffer), s, arguments);
	    vsprintf(buffer, s, arguments);
    va_end(arguments);

    bool status = m_console.IsLogging();
    m_console.SetLog( true );
    m_console.Print( buffer );  
    m_console.SetLog( status );
}